﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CoderJoe.DI
{
    public class DependencyContainer
        : IDependencyContainer
    {
        private IDependencyResolver NextResolver { get; set; }
        private List<DependencyRegistration> Registrations { get; set; }

        public DependencyContainer(IDependencyResolver nextResolver)
        {
            NextResolver = nextResolver;
            Registrations = new List<DependencyRegistration>();
        }

        #region IDependencyContainer Members

        public void RegisterSingleton(object dependency)
        {
            DependencyRegistration registration = new DependencyRegistration();
            registration.DependencyType = dependency.GetType();
            registration.Accessor = new Func<object>(delegate()
            {
                return dependency;
            });

            RegisterDependency(registration);
        }

        public void RegisterSingleRequest<TInstance>(Func<TInstance> constructor)
        {
            DependencyRegistration registration = new DependencyRegistration();
            registration.DependencyType = typeof(TInstance);
            registration.Accessor = new Func<object>(delegate()
            {
                return constructor();
            });

            RegisterDependency(registration);
        }

        private void RegisterDependency(DependencyRegistration registration)
        {
            Registrations.Add(registration);
        }

        #endregion

        #region IDependencyResolver Members

        public TInstance FindFirstService<TInstance>()
        {
            DependencyRegistration registration =
                (from r in Registrations
                 where r.DependencyType.IsAssignableFrom(typeof(TInstance))
                    || r.DependencyType.GetInterface(typeof(TInstance).FullName) != null
                 select r).FirstOrDefault();

            if (registration != null)
                return (TInstance)registration.Accessor();

            if (registration == null && NextResolver != null)
                return NextResolver.FindFirstService<TInstance>();

            return default(TInstance);
        }

        public IEnumerable<TInstance> FindAllServices<TInstance>()
        {
            IEnumerable<TInstance> instances =
                (from r in Registrations
                 where typeof(TInstance).IsAssignableFrom(r.DependencyType)
                    || r.DependencyType.GetInterface(typeof(TInstance).FullName) != null
                 select r.Accessor()).Cast<TInstance>();

            if (NextResolver != null)
                return instances.Union(NextResolver.FindAllServices<TInstance>());

            return instances;
        }

        #endregion
    }
}
